package common.activiti.web.action;

import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import javax.servlet.http.HttpServletResponse;

import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.task.IdentityLink;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONObject;

import common.web.action.CommonAction;
import common.web.vo.Result;

@Controller
@RequestMapping(value = "/bpm/deployment")
public class DeploymentController extends CommonAction {
	@Autowired
	RepositoryService repositoryService;

	@Autowired
	IdentityService identityService;

	@Autowired
	ManagementService managementService;

	private static final Logger logger = Logger.getLogger(DeploymentController.class);

	/**
	 * 流程定义列表
	 */
	@RequestMapping(value = "/list")
	@ResponseBody
	public Result processList() {
		ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
		List<ProcessDefinition> processDefinitionList = processDefinitionQuery.list();
		// 读取每个流程定义的候选属性
        Map<String, Map<String, List<? extends Object>>> linksMap = setCandidateUserAndGroups(processDefinitionList);
        
        Result reslut = Result.createResult(processDefinitionList);
        reslut.getMap().putAll(linksMap);
		return reslut;
	}
	
	
	/**
     * 流程定义列表--过滤激活的流程定义
     */
    @RequestMapping(value = "/list_active")
	@ResponseBody
    public Result processListReadonly() {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery.suspended().active();
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.list();

        // 读取每个流程定义的候选属性
        Map<String, Map<String, List<? extends Object>>> linksMap = setCandidateUserAndGroups(processDefinitionList);
        
        Result reslut = Result.createResult(processDefinitionList);
        reslut.getMap().putAll(linksMap);
		return reslut;
    }

	/**
	 * 流程定义状态控制
	 *
	 * @param state
	 *            active|suspend
	 * @param processDefinitionId
	 *            流程定义ID
	 * @return
	 */
	@RequestMapping(value = "/active", method = RequestMethod.POST)
	@ResponseBody
	public Result active(@RequestBody JSONObject json) {
		try {
			String processDefinitionId = json.getString("processDefinitionId");
			boolean cascadeProcessInstances = json.getBooleanValue("cascade");
			Date effectiveDate = json.getDate("effectiveDate");
			repositoryService.activateProcessDefinitionById(processDefinitionId, cascadeProcessInstances, effectiveDate);
		} catch (Exception e) {
			String msg = "激活流程失败！";
			// response.sendError(406, msg);
			logger.error(msg, e);
			// return null;
			return Result.fail(msg);
		}
		return Result.SUCCESS;
	}

	/**
	 * 流程定义状态控制
	 *
	 * @param state
	 *            active|suspend
	 * @param processDefinitionId
	 *            流程定义ID
	 * @return
	 */
	@RequestMapping(value = "/suspend", method = RequestMethod.POST)
	@ResponseBody
	public Result suspend(@RequestBody JSONObject json) {
		try {
			String processDefinitionId = json.getString("processDefinitionId");
			boolean cascadeProcessInstances = json.getBooleanValue("cascade");
			Date effectiveDate = json.getDate("effectiveDate");
			repositoryService.suspendProcessDefinitionById(processDefinitionId, cascadeProcessInstances, effectiveDate);
		} catch (Exception e) {
			String msg = "挂起流程失败！";
			// response.sendError(406, msg);
			logger.error(msg, e);
			// return null;
			return Result.fail(msg);
		}
		return Result.SUCCESS;
	}

	/**
	 * 删除部署的流程，级联删除流程实例
	 *
	 * @param deploymentId
	 *            流程部署ID
	 */
	@RequestMapping(value = "/delete/{deploymentId}")
	@ResponseBody
	public Result deleteProcessDefinition(@PathVariable("deploymentId") String deploymentId) {
		try {
			repositoryService.deleteDeployment(deploymentId, true);
			logger.info(MessageFormat.format("删除流程定义{0}", deploymentId));
		} catch (Exception e) {
			String msg = "删除流程定义失败！";
			// response.sendError(406, msg);
			logger.error(msg, e);
			// return null;
			return Result.fail(msg);
		}
		return Result.SUCCESS;
	}

	/**
	 * 部署流程资源
	 * 
	 * @throws IOException
	 */
	@RequestMapping(value = "/deploy")
	@ResponseBody
	public Result deploy(@RequestParam(value = "file", required = true) MultipartFile file, HttpServletResponse response)
			throws IOException {

		// 获取上传的文件名
		String fileName = file.getOriginalFilename();

		try {
			// 得到输入流（字节流）对象
			InputStream fileInputStream = file.getInputStream();

			// 文件的扩展名
			String extension = FilenameUtils.getExtension(fileName);

			// zip或者bar类型的文件用ZipInputStream方式部署
			DeploymentBuilder deployment = repositoryService.createDeployment();
			if (extension.equals("zip") || extension.equals("bar")) {
				ZipInputStream zip = new ZipInputStream(fileInputStream);
				deployment.addZipInputStream(zip);
			} else {
				// 其他类型的文件直接部署
				deployment.addInputStream(fileName, fileInputStream);
			}
			Deployment deploy = deployment.deploy();
			if (null == deploy) {
				String msg = "请选择流程定义文件部署";
				// response.sendError(406, msg);
				// return null;
				return Result.fail(msg);
			}
		} catch (Exception e) {
			String msg = "部署流程错误, 请检查文件";
			// response.sendError(406, msg);
			logger.error(msg, e);
			// return null;
			return Result.fail(msg);
		}

		return Result.SUCCESS;
	}
	
    /**
     * 读取流程定义的相关候选启动人、组，根据link信息转换并封装为User、Group对象
     * @param processDefinitionList
     * @return
     */
    private Map<String, Map<String, List<? extends Object>>> setCandidateUserAndGroups(List<ProcessDefinition> processDefinitionList) {
        Map<String, Map<String, List<? extends Object>>> linksMap = new HashMap<String, Map<String, List<? extends Object>>>();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            List<IdentityLink> identityLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinition.getId());

            Map<String, List<? extends Object>> single = new Hashtable<String, List<? extends Object>>();
            List<User> linkUsers = new ArrayList<User>();
            List<Group> linkGroups = new ArrayList<Group>();

            for (IdentityLink link : identityLinks) {
                if (StringUtils.isNotBlank(link.getUserId())) {
                    linkUsers.add(identityService.createUserQuery().userId(link.getUserId()).singleResult());
                } else if (StringUtils.isNotBlank(link.getGroupId())) {
                    linkGroups.add(identityService.createGroupQuery().groupId(link.getGroupId()).singleResult());
                }
            }

            single.put("user", linkUsers);
            single.put("group", linkGroups);

            linksMap.put(processDefinition.getId(), single);

        }
        return linksMap;
    }
}
